package phaseutil

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"

	"github.com/pkg/errors"
	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkenode "gopkg.openfuyao.cn/bkecommon/cluster/node"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"sigs.k8s.io/cluster-api/util/version"
	"sigs.k8s.io/controller-runtime/pkg/client"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/annotation"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/clusterutil"
)

const (
	DefaultRightVersionFields = 4
)

var (
	OnceFunc sync.Once
)

func NodeInfo(node confv1beta1.Node) string {
	if node.Hostname == "" {
		return node.IP
	}
	if node.IP == "" {
		return node.Hostname
	}
	return fmt.Sprintf("%s/%s", node.Hostname, node.IP)
}

func NodeRoleString(node confv1beta1.Node) string {
	return strings.Join(node.Role, ";")
}

func IsMasterNode(node *confv1beta1.Node) bool {
	for _, role := range node.Role {
		if role == bkenode.MasterNodeRole {
			return true
		}
	}
	return false
}

func IsWorkerNode(node *confv1beta1.Node) bool {
	for _, role := range node.Role {
		if role == bkenode.WorkerNodeRole {
			return true
		}
	}
	return false
}

func IsEtcdNode(node *confv1beta1.Node) bool {
	for _, role := range node.Role {
		if role == bkenode.EtcdNodeRole {
			return true
		}
	}
	return false
}

func GetBKENodesFromNodesStatus(nodesStatus confv1beta1.NodesStates) bkenode.Nodes {
	bkeNodes := bkenode.Nodes{}
	for _, nodeStatus := range nodesStatus {
		bkeNodes = append(bkeNodes, nodeStatus.Node)
	}
	return bkeNodes
}

func ConvertELBNodesToBKENodes(elbNodes []string, src bkenode.Nodes) bkenode.Nodes {
	var nodes bkenode.Nodes
	for _, ip := range elbNodes {
		for _, node := range src {
			if node.IP == ip {
				nodes = append(nodes, node)
			}
		}
	}
	return nodes
}

// GetNeedPushAgentNodes 获取需要推送agent的节点
func GetNeedPushAgentNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	for _, nodeStatus := range bkeCluster.Status.NodesStatus {
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeFailedFlag) {
			continue
		}
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeDeletingFlag) {
			continue
		}
		if !bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeAgentPushedFlag) {
			nodes = append(nodes, nodeStatus.Node)
		}
	}

	appointmentNodes := GetAppointmentAddNodes(bkeCluster)
	if appointmentNodes.Length() == 0 {
		return nodes
	}

	finalNodes := bkenode.Nodes{}
	for _, node := range nodes {
		if appointmentNodes.Filter(bkenode.FilterOptions{"IP": node.IP}).Length() == 0 {
			finalNodes = append(finalNodes, node)
		}
	}

	return finalNodes
}

// GetAgentPushedNodes 获取已推送agent的节点
func GetAgentPushedNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	for _, nodeStatus := range bkeCluster.Status.NodesStatus {
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeAgentPushedFlag) {
			nodes = append(nodes, nodeStatus.Node)
		}
	}
	return nodes
}

// GetNeedInitEnvNodes 获取需要初始化环境的节点
func GetNeedInitEnvNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	for _, nodeStatus := range bkeCluster.Status.NodesStatus {
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeFailedFlag) {
			continue
		}
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeDeletingFlag) {
			continue
		}
		if !bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeEnvFlag) {
			nodes = append(nodes, nodeStatus.Node)
		}
	}

	appointmentNodes := GetAppointmentAddNodes(bkeCluster)
	if appointmentNodes.Length() == 0 {
		return nodes
	}

	finalNodes := bkenode.Nodes{}
	for _, node := range nodes {
		if appointmentNodes.Filter(bkenode.FilterOptions{"IP": node.IP}).Length() == 0 {
			finalNodes = append(finalNodes, node)
		}
	}

	return finalNodes
}

// ----------join--------------------

// GetNeedJoinNodes 获取需要加入集群的节点
func GetNeedJoinNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	for _, nodeStatus := range bkeCluster.Status.NodesStatus {
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeFailedFlag) {
			continue
		}
		if bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeDeletingFlag) {
			continue
		}
		// 没有标记boot和init的节点
		if !bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.NodeBootFlag) && !bkeCluster.GetNodeStateFlag(nodeStatus.Node.IP, bkev1beta1.MasterInitFlag) {
			nodes = append(nodes, nodeStatus.Node)
		}
	}
	return nodes
}

// GetNeedJoinMasterNodes 获取需要加入集群的master节点
func GetNeedJoinMasterNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	needAddNodes := GetNeedJoinNodes(bkeCluster).Master()
	if needAddNodes.Length() == 0 {
		return nodes
	}

	appointmentNodes := GetAppointmentAddNodes(bkeCluster)
	if appointmentNodes.Length() > 0 {
		return ComputeFinalAddNodes(needAddNodes, appointmentNodes)
	}

	return needAddNodes
}

// GetNeedJoinWorkerNodes 获取需要加入集群的worker节点
func GetNeedJoinWorkerNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	needAddNodes := GetNeedJoinNodes(bkeCluster).Worker()
	if needAddNodes.Length() == 0 {
		return nodes
	}

	appointmentNodes := GetAppointmentAddNodes(bkeCluster)
	if appointmentNodes.Length() > 0 {
		return ComputeFinalAddNodes(needAddNodes, appointmentNodes)
	}

	return needAddNodes
}

// GetAppointmentAddNodes 获取预约加入的节点
func GetAppointmentAddNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	v, found := annotation.HasAnnotation(bkeCluster, annotation.AppointmentAddNodesAnnotationKey)
	if !found {
		return
	}
	nodesIP := strings.Split(v, ",")

	statusNodes := GetBKENodesFromNodesStatus(bkeCluster.Status.NodesStatus)
	for _, node := range statusNodes {
		if utils.ContainsString(nodesIP, node.IP) {
			nodes = append(nodes, node)
		}
	}
	return nodes
}

// ComputeFinalAddNodes 计算最终加入的节点
func ComputeFinalAddNodes(needAddNodes bkenode.Nodes, appointmentNodes bkenode.Nodes) (nodes bkenode.Nodes) {
	for _, node := range needAddNodes {
		// 如果需要加入的节点非预约节点则需要现在安装
		if appointmentNodes.Filter(bkenode.FilterOptions{"IP": node.IP}).Length() == 0 {
			nodes = append(nodes, node)
		}
	}
	return nodes
}

// ----------delete--------------------

// GetNeedDeleteNodes 获取需要删除的节点
func GetNeedDeleteNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	statusNodes := GetBKENodesFromNodesStatus(bkeCluster.Status.NodesStatus)
	nodeTs, ok := bkenode.CompareBKEConfigNode(statusNodes, bkeCluster.Spec.ClusterConfig.Nodes)
	if !ok {
		return
	}
	for _, node := range nodeTs {
		if node.Operate == bkenode.RemoveNode {
			nodes = append(nodes, *node.Node)
		}
	}
	return
}

// GetNeedDeleteWorkerNodes 获取需要删除的worker节点
func GetNeedDeleteWorkerNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	appointmentNodes := GetAppointmentDeletedNodes(bkeCluster)
	if appointmentNodes.Length() == 0 {
		return nodes
	}
	needDepleteNodes := GetNeedDeleteNodes(bkeCluster).Worker()
	if needDepleteNodes.Length() == 0 {
		return nodes
	}
	return ComputeFinalDeleteNodes(needDepleteNodes, appointmentNodes)
}

// GetNeedDeleteMasterNodes 获取需要删除的master节点
func GetNeedDeleteMasterNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	appointmentNodes := GetAppointmentDeletedNodes(bkeCluster)
	if appointmentNodes.Length() == 0 {
		return nodes
	}
	needDepleteNodes := GetNeedDeleteNodes(bkeCluster).Master()
	if needDepleteNodes.Length() == 0 {
		return nodes
	}
	return ComputeFinalDeleteNodes(needDepleteNodes, appointmentNodes)
}

// GetAppointmentDeletedNodes 从注释中获取预约删除的节点
func GetAppointmentDeletedNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	v, found := annotation.HasAnnotation(bkeCluster, annotation.AppointmentDeletedNodesAnnotationKey)
	if !found {
		return
	}
	nodesIP := strings.Split(v, ",")

	statusNodes := GetBKENodesFromNodesStatus(bkeCluster.Status.NodesStatus)
	for _, node := range statusNodes {
		if utils.ContainsString(nodesIP, node.IP) {
			nodes = append(nodes, node)
		}
	}
	return nodes
}

func RemoveAppointmentDeletedNodes(bkeCluster *bkev1beta1.BKECluster, nodeIP string) {
	v, found := annotation.HasAnnotation(bkeCluster, annotation.AppointmentDeletedNodesAnnotationKey)
	if !found {
		return
	}
	nodesIP := strings.Split(v, ",")
	for i, ip := range nodesIP {
		if ip == nodeIP {
			nodesIP = append(nodesIP[:i], nodesIP[i+1:]...)
			break
		}
	}
	annotation.SetAnnotation(bkeCluster, annotation.AppointmentDeletedNodesAnnotationKey, strings.Join(nodesIP, ","))
}

// ComputeFinalDeleteNodes 计算最终需要删除的节点
func ComputeFinalDeleteNodes(needDepleteNodes bkenode.Nodes, appointmentNodes bkenode.Nodes) (nodes bkenode.Nodes) {
	for _, node := range needDepleteNodes {
		if appointmentNodes.Filter(bkenode.FilterOptions{"IP": node.IP}).Length() != 0 {
			nodes = append(nodes, node)
		}
	}
	return nodes
}

// ----------upgrade--------------------

// NormalizeVersion 将版本号归一化为 vX.Y.Z 格式，去除前导零
func NormalizeVersion(version string) (string, error) {
	// 移除可能的 "v" 前缀并转换为小写
	clean := strings.TrimPrefix(strings.ToLower(version), "v")

	// 使用正则表达式提取数字部分
	re := regexp.MustCompile(`(\d+)\.?(\d*)\.?(\d*)`)
	matches := re.FindStringSubmatch(clean)

	if len(matches) < DefaultRightVersionFields {
		return "", fmt.Errorf("invalid version format: %s", version)
	}

	parts := make([]string, DefaultRightVersionFields-1)
	for i := 1; i < DefaultRightVersionFields; i++ {
		part := matches[i]
		if part == "" {
			part = "0"
		}

		num, err := strconv.Atoi(part)
		if err != nil {
			return "", fmt.Errorf("invalid number in version: %s", part)
		}
		parts[i-1] = strconv.Itoa(num)
	}

	return "v" + strings.Join(parts, "."), nil
}

// GetNeedUpgradeNodes 获取需要升级的节点
func GetNeedUpgradeNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	newConfig := bkeCluster.Spec.ClusterConfig

	if bkeCluster.Status.OpenFuyaoVersion == "" {
		return nil
	}

	oldVersion, err := NormalizeVersion(bkeCluster.Status.OpenFuyaoVersion)
	if err != nil {
		return nil
	}
	oldv, err := version.ParseMajorMinorPatch(oldVersion)
	if err != nil {
		return nil
	}

	newVersion, err := NormalizeVersion(newConfig.Cluster.OpenFuyaoVersion)
	if err != nil {
		return nil
	}
	newv, err := version.ParseMajorMinorPatch(newVersion)
	if err != nil {
		return nil
	}

	// step 2 compare cluster version upgrade
	switch version.Compare(newv, oldv) {
	case -1:
		// do nothing
	case 0:
		// do nothing
	case 1:
		statusNodes := GetBKENodesFromNodesStatus(bkeCluster.Status.NodesStatus)
		for _, node := range statusNodes {
			if bkeCluster.GetNodeStateFlag(node.IP, bkev1beta1.NodeFailedFlag) {
				continue
			}
			nodes = append(nodes, node)
		}

		return nodes
	}
	return nil
}

// GetNeedUpgradeMasterNodes 获取需要升级的master节点
func GetNeedUpgradeMasterNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	return GetNeedUpgradeNodes(bkeCluster).Master()
}

// GetNeedUpgradeWorkerNodes 获取需要升级的worker节点
func GetNeedUpgradeWorkerNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	return GetNeedUpgradeNodes(bkeCluster).Worker()
}

// ----------loadbalance--------------------

// GetNeedLoadBalanceNodes 获取需要负载均衡的节点
func GetNeedLoadBalanceNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {

	needJoinMaster := GetNeedJoinMasterNodes(bkeCluster)
	needDeleteMaster := GetNeedDeleteMasterNodes(bkeCluster)

	// 获取两者交集
	nodesMap := make(map[string]confv1beta1.Node)
	for _, node := range needJoinMaster {
		nodesMap[node.IP] = node
	}
	for _, node := range needDeleteMaster {
		nodesMap[node.IP] = node
	}
	for _, node := range nodesMap {
		nodes = append(nodes, node)
	}
	return nodes

}

// ----------bke-config--------------------

func GetBKEConfigCMData(ctx context.Context, c client.Client) (map[string]string, error) {
	config := &corev1.ConfigMap{}
	err := c.Get(ctx, clusterutil.BKEConfigCmKey(), config)
	if err != nil {
		return nil, err
	}
	return config.Data, nil
}

func GetRemoteBKEConfigCM(ctx context.Context, clientSet *kubernetes.Clientset) (*corev1.ConfigMap, error) {
	config, err := clientSet.CoreV1().ConfigMaps(clusterutil.BKEConfigCmKey().Namespace).Get(ctx, clusterutil.BKEConfigCmKey().Name, metav1.GetOptions{})
	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, nil
		}
		return nil, err
	}
	return config, nil
}

func MigrateBKEConfigCM(ctx context.Context, c client.Client, clientSet *kubernetes.Clientset) error {
	config := &corev1.ConfigMap{}
	err := c.Get(ctx, clusterutil.BKEConfigCmKey(), config)
	if err != nil {
		return err
	}
	remoteConfig := &corev1.ConfigMap{}
	remoteConfig.Name = clusterutil.BKEConfigCmKey().Name
	remoteConfig.Namespace = clusterutil.BKEConfigCmKey().Namespace
	remoteConfig.Data = config.Data

	// create ns
	ns := &corev1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: remoteConfig.Namespace,
		},
	}

	_, err = clientSet.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
	if err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return errors.Errorf("create remote bke config ns %q failed: %v", err, remoteConfig.Namespace)
		}
	}

	_, err = clientSet.CoreV1().ConfigMaps(remoteConfig.Namespace).Create(ctx, remoteConfig, metav1.CreateOptions{})
	if err != nil {
		if apierrors.IsAlreadyExists(err) {
			_, err = clientSet.CoreV1().ConfigMaps(remoteConfig.Namespace).Update(ctx, remoteConfig, metav1.UpdateOptions{})
			if err != nil {
				return errors.Errorf("update remote bke config cm failed: %v", err)
			}
			return nil
		}
		return errors.Errorf("create remote bke config cm failed: %v", err)
	}
	return nil
}

// ----------bootstrap--------------------

func GetReadyBootstrapNodes(bkeCluster *bkev1beta1.BKECluster) (nodes bkenode.Nodes) {
	statusNodes := GetBKENodesFromNodesStatus(bkeCluster.Status.NodesStatus)
	for _, node := range statusNodes {
		// agent is ready and env is ready and boot is not
		if bkeCluster.GetNodeStateFlag(node.IP, bkev1beta1.NodeAgentReadyFlag) &&
			bkeCluster.GetNodeStateFlag(node.IP, bkev1beta1.NodeEnvFlag) &&
			!bkeCluster.GetNodeStateFlag(node.IP, bkev1beta1.NodeBootFlag) {
			nodes = append(nodes, node)
		}
	}
	return nodes
}

func GetRemoteNodeByBKENode(ctx context.Context, clientSet *kubernetes.Clientset, node confv1beta1.Node) (*corev1.Node, error) {
	return clientSet.CoreV1().Nodes().Get(ctx, node.Hostname, metav1.GetOptions{})
}
